﻿using Reader.Chromely.Core.Logging;
using Reader.Chromely.Core.Network;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Dynamic;
using System.Linq;
using System.Reflection;
using System.Text.Json;
using System.Xml.Schema;
using Xilium.CefGlue;


namespace Reader.Core.Chromely.Core.ProcessMessage
{
    public static class ValueTransform
    {
        #region CefToCSharp
        public static object[] ToCSharpValue(this CefV8Value[] source, MethodInfo infos)
        {
            if(source.Length == 0)
            {
                return null;
            }
            object[] target = new object[source.Length];
            var parameter = infos.GetParameters();
            for(var i = 0; i < source.Length; i++)
            {
                if (source[i].IsInt)
                {
                    target[i] = source[i].GetIntValue();
                }
                else if (source[i].IsUInt)
                {
                    target[i] = source[i].GetUIntValue();
                }
                else if (source[i].IsBool)
                {
                    target[i] = source[i].GetBoolValue();
                }
                else if (source[i].IsString)
                {
                    target[i] = source[i].GetStringValue();
                }
                else if (source[i].IsDouble)
                {
                    target[i] = source[i].GetDoubleValue();
                }
                else if(source[i].IsNull || source[i].IsUndefined)
                {
                    target[i] = null;
                }
                else if (source[i].IsDate)
                {
                    target[i] = source[i].GetDateValue();
                }
                else if (source[i].IsArray)
                {
                    var json = JsonSerializer.Serialize(source[i].ToCSharpArrayValue());
                    target[i] = JsonSerializer.Deserialize(json, parameter[i].ParameterType);
                    //target[i] = source[i].ToCSharpArrayValue();
                }
                else if (source[i].IsObject)
                {
                    var json = JsonSerializer.Serialize(source[i].ToCSharpObjectValue());
                    target[i] = JsonSerializer.Deserialize(json, parameter[i].ParameterType);
                }
            }
            return target;
        }
        public static Array ToCSharpArrayValue(this CefV8Value source)
        {
            Debug.Assert(source.IsArray);

            Type t = typeof(object);
            var length = source.GetArrayLength();
            ArrayList target = new ArrayList(length);
            for (var i = 0; i < length; i++)
            {
                CefV8Value value = source.GetValue(i);
                if (value.IsInt)
                {
                    t = typeof(int);
                    target.Add(value.GetIntValue());
                }
                else if (value.IsUInt)
                {
                    t = typeof(uint);
                    target.Add(value.GetUIntValue());
                }
                else if (value.IsBool)
                {
                    t = typeof(bool);
                    target.Add(value.GetBoolValue());
                }
                else if (value.IsString)
                {
                    t = typeof(string);
                    target.Add(value.GetStringValue());
                }
                else if (value.IsDouble)
                {
                    t = typeof(double);
                    target.Add(value.GetDoubleValue());
                }
                else if (value.IsNull || value.IsUndefined)
                {
                    target.Add(null);
                }
                else if (value.IsDate)
                {
                    t = typeof(DateTime);
                    target.Add(value.GetDateValue());
                }
                else if (value.IsArray)
                {
                    t = typeof(Array);
                    target.Add(value.ToCSharpArrayValue());
                }
                else if (value.IsObject)
                {
                    var json = JsonSerializer.Serialize(value.ToCSharpObjectValue(), typeof(object));
                    var obj = JsonSerializer.Deserialize(json, typeof(object));
                    target.Add(obj);
                }
            }
            return target.ToArray(t);
        }
        public static object ToCSharpObjectValue(this CefV8Value source)
        {
            Debug.Assert(source.IsObject);
            List<string> keys = source.GetKeys().ToList();
            dynamic target = new ExpandoObject();
            var dic = (IDictionary<string, object>)target;
            foreach(var key in keys)
            {
                CefV8Value value = source.GetValue(key);
                if (value.IsInt)
                {
                    dic[key] = value.GetIntValue();
                }
                else if (value.IsUInt)
                {
                    dic[key] = value.GetUIntValue();
                }
                else if (value.IsBool)
                {
                    dic[key] = value.GetBoolValue();
                }
                else if (value.IsString)
                {
                    dic[key] = value.GetStringValue();
                }
                else if (value.IsDouble)
                {
                    dic[key] = value.GetDoubleValue();
                }
                else if (value.IsNull || value.IsUndefined)
                {
                    dic[key] = null;
                }
                else if (value.IsDate)
                {
                    dic[key] = value.GetDateValue();
                }
                else if (value.IsArray)
                {
                    dic[key] = value.ToCSharpArrayValue();
                }
                else if (value.IsObject)
                {
                    dic[key] = value.ToCSharpObjectValue();
                }
            }
            return (object)target;
        }
        public static object[] ToCSharpValue(this CefListValue source)
        {
            if (source.Count == 0)
            {
                return null;
            }
            var target = new object[source.Count];
            var length = source.Count;
            for (var i = 0; i < length; i++)
            {
                var type = source.GetValueType(i);
                switch (type)
                {
                    case CefValueType.Bool:
                        target[i] = source.GetBool(i);
                        break;
                    case CefValueType.Double:
                        target[i] = source.GetDouble(i);
                        break;
                    case CefValueType.Int:
                        target[i] = source.GetInt(i);
                        break;
                    case CefValueType.String:
                        target[i] = source.GetString(i);
                        break;
                    case CefValueType.Null:
                        target[i] = null;
                        break;
                    case CefValueType.List:
                        target[i] = source.GetList(i).ToCSharpValue();
                        break;
                    case CefValueType.Dictionary:
                        target[i] = source.GetDictionary(i).ToCSHarpObjectValue();
                        break;
                    default:
                        break;
                }
            }
            return target;
        }
        public static object ToCSHarpObjectValue(this CefDictionaryValue source)
        {
            if(source.Count == 0)
            {
                return null;
            }
            List<string> keys = source.GetKeys().ToList();
            dynamic target = new ExpandoObject();
            var dic = (IDictionary<string, object>)target;
            foreach (var key in keys)
            {
                var type = source.GetValueType(key);
                switch (type)
                {
                    case CefValueType.Bool:
                        target[key] = source.GetBool(key);
                        break;
                    case CefValueType.Double:
                        target[key] = source.GetDouble(key);
                        break;
                    case CefValueType.Int:
                        target[key] = source.GetInt(key);
                        break;
                    case CefValueType.String:
                        target[key] = source.GetString(key);
                        break;
                    case CefValueType.Null:
                        target[key] = null;
                        break;
                    case CefValueType.List:
                        target[key] = source.GetList(key).ToCSharpValue();
                        break;
                    case CefValueType.Dictionary:
                        target[key] = source.GetDictionary(key).ToCSHarpObjectValue();
                        break;
                    default:
                        break;
                }
            }
            return (object)target;
        }
        #endregion
        #region CSharpToCef
        public static CefV8Value ToCefV8Value(dynamic source)
        {
            if(source is null)
            {
                return null;
            }
            var type = source.GetType();
            Logger.Instance.Log.Debug($"type:{type.Name.ToLower()}\nis null:{source is null}\nis array or ilist:{source is Array || source is IList}\nis object:{source is object}");
            if (source is null) return CefV8Value.CreateNull();
            if (source is Array || source is IList) return ToCefV8ArrayValue(source);
            switch (type.Name.ToLower())
            {
                case "string":
                case "char":
                case "char[]":
                case "long":
                case "ulong":
                case "decimal":
                    return CefV8Value.CreateString((string)source);
                case "byte":
                case "sbyte":
                case "short":
                case "ushort":
                case "int":
                case "int32":
                    return CefV8Value.CreateInt((int)source);
                case "uint":
                case "uint16":
                    return CefV8Value.CreateUInt((uint)source);
                case "bool":
                    return CefV8Value.CreateBool((bool)source);
                case "double":
                case "float":
                    return CefV8Value.CreateDouble((double)source);
                case "void":
                    return null;
                default: //object
                    return ToCefV8ObjectValue(source);
            }
        }
        public static CefV8Value ToCefV8ArrayValue(dynamic source)
        {
            Debug.Assert(source is Array || source is IList);
            if(source is Array || source is IList)
            {
                var sourceArray = (IList)source;
                var target = CefV8Value.CreateArray(sourceArray.Count);
                for(var i = 0; i < sourceArray.Count; i++)
                {
                    target.SetValue(i, ToCefV8Value(sourceArray[i]));
                }
                return target;
            }
            else
            {
                return source.ToCefV8ObjectValue();
            }
            
        }
        public static CefV8Value ToCefV8ObjectValue(dynamic source)
        {
            Debug.Assert(source is object);
            if(source is object)
            {
                var target = CefV8Value.CreateObject();
                foreach (var property in source.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public))
                {
                    var name = property.Name;
                    var value = property.GetValue(source);
                    if(value is null)
                    {
                        target.SetValue(name, CefV8Value.CreateNull());
                    }
                    else
                    {
                        target.SetValue(name, ToCefV8Value(value));
                    }
                }
                return target;
            }
            else
            {
                throw new Exception($"Not Support Data Type：{source.GetType().FullName}");
            }
        }
        #endregion
        #region CefV8ToCefListValue
        public static CefV8Value ToCefV8ValueJson(this object source)
        {
            var json = JsonSerializer.Serialize(source, new JsonSerializerOptions()
            {
                IgnoreReadOnlyProperties = true,
                ReadCommentHandling = JsonCommentHandling.Skip
            });
            var value = CefV8Value.CreateString(json);
            return value;
        }
        public static void SetCefV8Value(this CefListValue target, CefV8Value[] source)
        {
            var length = source.Length;
            for (int i = 0; i < length; i++)
            {
                CefV8Value value = source[i];
                if (value.IsBool)
                {
                    target.SetBool(i, value.GetBoolValue());
                }
                else if (value.IsInt || value.IsUInt)
                {
                    target.SetInt(i, value.GetIntValue());
                }
                else if (value.IsDouble)
                {
                    target.SetDouble(i, value.GetDoubleValue());
                }
                else if (value.IsNull || value.IsUndefined)
                {
                    target.SetNull(i);
                }
                else if (value.IsString || value.IsDate)
                {
                    target.SetString(i, value.GetStringValue());
                }
                else if (value.IsArray)
                {
                    CefListValue list = CefListValue.Create();
                    list = value.ToCefListValue();
                    target.SetList(i, list);
                }
                else if (value.IsObject)
                {
                    var dic = CefDictionaryValue.Create();
                    dic = value.ToCefDictionaryValue();
                    target.SetDictionary(i, dic);
                }
            }
        }
        public static CefListValue ToCefListValue(this CefV8Value source)
        {
            Debug.Assert(source.IsArray);

            CefListValue target = CefListValue.Create();

            var length = source.GetArrayLength();
            target.SetSize(length);

            for(int i = 0; i < length; i++)
            {
                CefV8Value value = source.GetValue(i);
                if (value.IsBool)
                {
                    target.SetBool(i, value.GetBoolValue());
                }
                else if (value.IsInt || value.IsUInt)
                {
                    target.SetInt(i, value.GetIntValue());
                }
                else if (value.IsDouble)
                {
                    target.SetDouble(i, value.GetDoubleValue());
                }
                else if (value.IsNull || value.IsUndefined)
                {
                    target.SetNull(i);
                }
                else if (value.IsString || value.IsDate)
                {
                    target.SetString(i, value.GetStringValue());
                }
                else if (value.IsArray)
                {
                    CefListValue list = CefListValue.Create();
                    list = value.ToCefListValue();
                    target.SetList(i, list);
                }
                else if (value.IsObject)
                {
                    var dic = CefDictionaryValue.Create();
                    dic = value.ToCefDictionaryValue();
                    target.SetDictionary(i, dic);
                }
            }
            return target;
        }
        public static CefDictionaryValue ToCefDictionaryValue(this CefV8Value source)
        {
            Debug.Assert(source.IsObject);

            CefDictionaryValue target = CefDictionaryValue.Create();
            var keys = source.GetKeys();
            foreach (var key in keys)
            {
                var value = source.GetValue(key);
                if (value.IsBool)
                {
                    target.SetBool(key, value.GetBoolValue());
                }
                else if (value.IsInt || value.IsUInt)
                {
                    target.SetInt(key, value.GetIntValue());
                }
                else if (value.IsDouble)
                {
                    target.SetDouble(key, value.GetDoubleValue());
                }
                else if (value.IsNull || value.IsUndefined)
                {
                    target.SetNull(key);
                }
                else if (value.IsString || value.IsDate)
                {
                    target.SetString(key, value.GetStringValue());
                }
                else if (value.IsArray)
                {
                    CefListValue list = CefListValue.Create();
                    list = value.ToCefListValue();
                    target.SetList(key, list);
                }
                else if (value.IsObject)
                {
                    var dic = CefDictionaryValue.Create();
                    dic = value.ToCefDictionaryValue();
                    target.SetDictionary(key, dic);
                }
            }
            return target;
        }
        #endregion
    }
}
